import atexit

from PySide6.QtCore import QObject, Property, Slot, Signal
from PySide6.QtQml import QmlElement
from src.common.signal_bus import SignalBus
from src.library.model import MusicMeta
from src.model import MusicListModel
from src.thread.base_thread import TaskWorker
from src.utils.cache import load_item, cache_item
from src.utils.debug_tools import print_debug  # noqa

QML_IMPORT_NAME = "Backend"
QML_IMPORT_MAJOR_VERSION = 1
CACHE_PLAY_QUEUE_NAME = "play_queue"
CACHE_LAST_INDEX_NAME = "play_queue_index"


# 判断该音乐是否已经在播放列表中
def _has_in_list(music: MusicMeta, play_queue: list[MusicMeta]) -> bool:
    for m in play_queue:
        if m.play_id == music.play_id:
            return play_queue.index(m)
    return -1


@QmlElement
class PlaylistViewModel(QObject):
    currentIndexChanged = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self._start_load_play_queue()
        self._signalBus = SignalBus()
        self._bind_bus()
        self._playlist_model: MusicListModel = MusicListModel([])
        self._playlist_model.countChanged.connect(
            lambda: self._signalBus.playlistTotalChanged.emit(self._playlist_model.count)
        )
        self._current_music: MusicMeta = None
        atexit.register(lambda: self.__save_play_queue())

    @Property(MusicListModel, constant=True)
    def playlistModel(self):
        return self._playlist_model

    @Property(int, notify=currentIndexChanged)
    def currentIndex(self):
        if not self._playlist_model.musics:
            return -1
        return self._playlist_model.musics.index(self._current_music) if self._current_music else -1

    @currentIndex.setter
    def currentIndex(self, index):
        if index < 0 or index >= len(self._playlist_model.musics):
            return
        self._current_music = self._playlist_model.musics[index]
        self._signalBus.playMusic.emit(self._current_music)
        self.currentIndexChanged.emit()

    ########################################################
    ## 与QML绑定的方法
    ########################################################
    @Slot()
    def clearAll(self):
        self._playlist_model.reset([])
        self._current_music = None
        self._signalBus.resetPlayer.emit()

    @Slot(int)
    def removeMusic(self, index):
        if index < 0 or index >= len(self._playlist_model.musics):
            return
        current_index = self._playlist_model.musics.index(self._current_music)
        if index == current_index:
            # 如果删除的是当前播放的音乐,则播放下一首
            self._signalBus.nextMusic.emit()
        self._playlist_model.remove(index)

    ########################################################
    # 与其他viewModel通信的接口
    ########################################################
    def _bind_bus(self):
        self._signalBus.nextMusic.connect(self._next_music)
        self._signalBus.preMusic.connect(self._pre_music)
        self._signalBus.addMusic.connect(self._add_music)

    ########################################################
    # 私有方法
    ########################################################
    def _start_load_play_queue(self):

        def slot(result):
            play_queue, last_index = result
            self._playlist_model.reset(play_queue)
            self.currentIndex = last_index

        def faild():
            pass

        workder = TaskWorker(self.__load_play_queue)
        workder.success.connect(slot)
        workder.faild.connect(faild)
        workder.finished.connect(lambda _w=workder: _w.deleteLater())
        workder.start()

    @staticmethod
    def __load_play_queue():
        raw_play_queue: list[MusicMeta] = load_item(CACHE_PLAY_QUEUE_NAME)
        if not raw_play_queue:
            return [], -1
        play_index = load_item(CACHE_LAST_INDEX_NAME)

        if play_index is None:
            play_index = -1

        return raw_play_queue, play_index

    def __save_play_queue(self):
        cache_item(CACHE_PLAY_QUEUE_NAME, self._playlist_model.musics)
        last_index = self._playlist_model.musics.index(self._current_music) if self._current_music else -1
        cache_item(CACHE_LAST_INDEX_NAME, last_index)

    def _add_music(self, music: MusicMeta, play: bool = False):
        index = _has_in_list(music, self._playlist_model.musics)
        if index == -1:
            index = self._insert_music(music)
        if play:
            self.currentIndex = index

    def _next_music(self):
        if not self._current_music:
            return
        index = self._playlist_model.musics.index(self._current_music)
        if index + 1 >= len(self._playlist_model.musics):
            self.currentIndex = 0
        else:
            self.currentIndex = index + 1

    def _pre_music(self):
        if not self._current_music:
            return
        index = self._playlist_model.musics.index(self._current_music)
        if index - 1 < 0:
            self.currentIndex = len(self._playlist_model.musics) - 1
        else:
            self.currentIndex = index - 1

    def _insert_music(self, music: MusicMeta):
        if not self._current_music:
            self._playlist_model.append(music)
            return 0
        insert_index = self._playlist_model.musics.index(self._current_music) + 1
        self._playlist_model.insert(insert_index, music)
        return insert_index
